home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 June / CHIP_CD_2004-06.iso / software / miranda_hit / files / mirinstsetup.exe / Miranda Installer 0.0.1.2 / MirandaInstaller.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-04-20  |  31.9 KB  |  1,022 lines

  1. /*
  2.     Miranda Installer - Installs nightlies and Miranda addons.
  3.     Copyright (C) 2002-2003 Goblineye Entertainment
  4.  
  5.     Authors: Saar (Tornado) and Kai (kai_b)
  6.  
  7.     This program is free software; you can redistribute it and/or modify
  8.     it under the terms of the GNU General Public License as published by
  9.     the Free Software Foundation; either version 2 of the License, or
  10.     (at your option) any later version.
  11.  
  12.     This program is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License
  18.     along with this program; if not, write to the Free Software
  19.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20. */
  21.  
  22. #include "MirandaInstaller.h"
  23. #pragma hdrstop
  24.  
  25. HINSTANCE g_hInstance = NULL; // globally tracked hinstance
  26. HANDLE g_hWorkThread = NULL; // handle to worker thread
  27.  
  28. byte g_bInstallFlags = 0; // installation flags. LSB is silent, 2 is restart when done
  29. bool g_fStopThread = false;
  30.  
  31. DWORD g_dwDdeInst = 0; // global DDE instance
  32. HWND g_hInstallDlg = NULL;
  33.  
  34. MAIN_OPTIONS g_WorkOptions; // guess what this is... :)
  35. HMENU g_hContextMenu = NULL; // context menu
  36. WNDPROC g_lpfListView = NULL; // listview control (subclassed for drag'n'drop)
  37.  
  38.  
  39.  
  40. LRESULT CALLBACK LVSubClassProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
  41. {
  42.     switch(uMsg)
  43.     {
  44.         case WM_DROPFILES:
  45.         {
  46.             HDROP hDrop = (HDROP)wParam;
  47.             char szPath[MAX_PATH + 1];
  48.             for (UINT index=0,iCount=DragQueryFile(hDrop,-1,NULL,0);index < iCount;index++)
  49.             {
  50.                 DragQueryFile(hDrop,index,szPath,MAX_PATH+1);
  51.                 
  52.                 if (lstrlen(szPath) > 0)
  53.                 {
  54.                     DWORD dwAtts = GetFileAttributes(szPath);
  55.  
  56.                     if (dwAtts == INVALID_FILE_ATTRIBUTES) // invalid
  57.                     {
  58.                         continue;
  59.                     }
  60.  
  61.                     if (dwAtts & FILE_ATTRIBUTE_DIRECTORY) // directory
  62.                     {
  63.                         if (*(szPath+lstrlen(szPath)-1) != '\\') // no backslash
  64.                         {
  65.                             lstrcat(szPath,"\\"); // must be backslashed already
  66.                         }
  67.                         InstallList_AddDirectory(GetParent(hWnd),szPath);
  68.                     }
  69.                     else // file
  70.                     {
  71.                         InstallList_AddFile(GetParent(hWnd),szPath);
  72.                     }
  73.                 }
  74.             }
  75.  
  76.             DragFinish(hDrop);
  77.             return(0);
  78.         } break;
  79.     }
  80.     return(CallWindowProc(g_lpfListView,hWnd,uMsg,wParam,lParam));
  81. }
  82.  
  83.  
  84.  
  85. void Parse_CommandLine(char *lpCmdLine)
  86. {
  87.     char *lpIndex = lpCmdLine;
  88.  
  89.     byte bInstallType = 0; // 0 - none, 1 - file, 2 - dir
  90.     bool fGotFileDir = false;
  91.     byte l_bInstallFlags = 0; // goes global if we got an installation
  92.     char szArgument[MAX_PATH + 1]; // maximum allowed size of an argument (this will hold the filename/directory when we're done)
  93.     while ((lpIndex != NULL) && (*lpIndex != '\0'))
  94.     {
  95.         char *lpNext;
  96.         if (*lpIndex == '\"') // if we got a double-quotes in there
  97.         {
  98.             lpNext = strchr(lpIndex+1,'\"');
  99.             if (lpNext) // isn't NULL, yay
  100.             {
  101.                 lpNext++;
  102.             }
  103.             else // NULL, crap
  104.             {
  105.                 bInstallType = 0; // not installing nothing
  106.                 break;
  107.             }
  108.         }
  109.         else
  110.         {
  111.             lpNext = strchr(lpIndex,' ');
  112.         }
  113.          
  114.         if (lpNext == NULL)
  115.         {
  116.             lpNext = lpIndex + lstrlen(lpIndex); // end
  117.         }
  118.  
  119.         char *lpCopyTo = lpNext;
  120.         // we copy lpCopyTo-lpIndex
  121.         if (lpCopyTo-lpIndex > 512)
  122.         {
  123.             lpCopyTo = lpIndex + 512;
  124.         }
  125.         lstrcpyn(szArgument,lpIndex,lpCopyTo - lpIndex + 1);
  126. //                szArgument[lpCopyTo - lpIndex] = '\0'; // NULL it (no need to, lstrcpyn does it for us)
  127.  
  128.         // now some checks
  129.         if (lstrcmpi(szArgument,"/silent") == 0) // silent
  130.         {
  131.             l_bInstallFlags |= 1;
  132.         }
  133.         else if (lstrcmpi(szArgument,"/restart") == 0) // restart when done
  134.         {
  135.             l_bInstallFlags |= 2;
  136.         }
  137.         else if (lstrcmpi(szArgument,"/directory") == 0)
  138.         {
  139.             bInstallType = 2;
  140.         }
  141.         else if (*lpNext == '\0') // if next is NULL, we got dir/filename
  142.         {
  143.             if (bInstallType == 0) // if we got nothing so far
  144.             {
  145.                 bInstallType = 1; // then it's file installation
  146.             }
  147.             fGotFileDir = true;
  148.         }
  149.  
  150.         if (*lpNext == ' ') lpNext++;
  151.  
  152.         lpIndex = lpNext;
  153.     }
  154.  
  155.     // now we process what we got from the command line
  156.     // we require both silent and restart, or just silent
  157.     if ((bInstallType) && (fGotFileDir)) // wee, we got something to install
  158.     {
  159.         if ((l_bInstallFlags == 0) || (l_bInstallFlags == 3) || (l_bInstallFlags == 1))
  160.         {
  161.             g_bInstallFlags = l_bInstallFlags;
  162.         }
  163.         else // no flags
  164.         {
  165.             g_bInstallFlags = 0;
  166.         }
  167.  
  168.         // check to see if we have double-quotes
  169.         if ((lstrlen(szArgument) > 2) && (*szArgument == '\"') && (*(szArgument+lstrlen(szArgument)-1) == '\"')) // double-quotes on both end
  170.         {
  171.             for (unsigned int index=1,iLen=lstrlen(szArgument)-1;index < iLen;index++)
  172.             {
  173.                 szArgument[index-1] = szArgument[index];
  174.             }
  175.             szArgument[index-1] = '\0';
  176.         }
  177.  
  178.         if (bInstallType == 1)
  179.         {
  180.             InstallList_AddFile(g_hInstallDlg,szArgument);
  181.         }
  182.         else
  183.         {
  184.             if (*(szArgument+lstrlen(szArgument)-1) != '\\') // no backslash
  185.             {
  186.                 lstrcat(szArgument,"\\"); // must be backslashed already
  187.             }
  188.             InstallList_AddDirectory(g_hInstallDlg,szArgument);
  189.         }
  190.     }
  191.  
  192.     // if g_fSilentInstall is true, then installation will be triggered here
  193.     if (g_bInstallFlags & 1)
  194.     {
  195.         SendDlgItemMessage(g_hInstallDlg,IDOK,BM_CLICK,0,0); // get to work! =D
  196.     }
  197. }
  198.  
  199.  
  200.  
  201. // no use for this one (dummy callback, used for client)
  202. HDDEDATA CALLBACK DdeDummyCallback(UINT uType,UINT uFmt,HCONV hconv,HDDEDATA hsz1,HDDEDATA hsz2,HDDEDATA hdata,HDDEDATA dwData1,HDDEDATA dwData2)
  203. {
  204.     return(FALSE);
  205. }
  206.  
  207.  
  208.  
  209. HDDEDATA CALLBACK DdeCallback(UINT uType,UINT uFmt,HCONV hconv,HDDEDATA hsz1,HDDEDATA hsz2,HDDEDATA hdata,HDDEDATA dwData1,HDDEDATA dwData2)
  210. {
  211.     switch(uType)
  212.     {
  213.         case XTYP_CONNECT:
  214.         {
  215.             // check topic
  216.             char szTemp[32];
  217.             if (!DdeQueryString(g_dwDdeInst,(HSZ)hsz1,szTemp,32,CP_WINANSI))
  218.             {
  219.                 return(FALSE);
  220.             }
  221.             if (lstrcmp(szTemp,DDE_TOPIC))
  222.             {
  223.                 return(FALSE);
  224.             }
  225.  
  226.             // check service
  227.             if (!DdeQueryString(g_dwDdeInst,(HSZ)hsz2,szTemp,32,CP_WINANSI))
  228.             {
  229.                 return(FALSE);
  230.             }
  231.             if (lstrcmp(szTemp,DDE_SERVICE))
  232.             {
  233.                 return(FALSE);
  234.             }
  235.  
  236.             return((HDDEDATA)TRUE);
  237.         } break;
  238.         case XTYP_POKE:
  239.         {
  240.             if (uFmt != CF_TEXT) // only text!
  241.             {
  242.                 return(DDE_FNOTPROCESSED);
  243.             }
  244.  
  245.             // check topic
  246.             char szTemp[32];
  247.             if (!DdeQueryString(g_dwDdeInst,(HSZ)hsz1,szTemp,32,CP_WINANSI))
  248.             {
  249.                 return(DDE_FNOTPROCESSED);
  250.             }
  251.             if (lstrcmp(szTemp,DDE_TOPIC))
  252.             {
  253.                 return(DDE_FNOTPROCESSED);
  254.             }
  255.  
  256.             // check item
  257.             if (!DdeQueryString(g_dwDdeInst,(HSZ)hsz2,szTemp,32,CP_WINANSI))
  258.             {
  259.                 return(DDE_FNOTPROCESSED);
  260.             }
  261.             if (lstrcmp(szTemp,DDE_ITEM))
  262.             {
  263.                 return(DDE_FNOTPROCESSED);
  264.             }
  265.  
  266.             // process data here...
  267.             char *lpCmdLine = (char*)DdeAccessData(hdata,NULL);
  268.             Parse_CommandLine(lpCmdLine);
  269.             DdeUnaccessData(hdata);
  270.  
  271.  
  272.             return((HDDEDATA)DDE_FACK);
  273.         } break;
  274.     }
  275.     return(NULL);
  276. }
  277.  
  278.  
  279.  
  280. INT_PTR CALLBACK InstallDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
  281. {
  282.     switch(uMsg)
  283.     {
  284.         case WM_INITDIALOG:
  285.         {
  286.             g_hInstallDlg = hwndDlg;
  287.             HICON hIcon = LoadIcon(g_hInstance,MAKEINTRESOURCE(IDI_DEFICON));
  288.             SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(LPARAM)hIcon);
  289.             SendMessage(hwndDlg,WM_SETICON,ICON_BIG,(LPARAM)hIcon);
  290.  
  291.             // ownerdraw
  292.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWL_STYLE,GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWL_STYLE) | SS_OWNERDRAW);
  293.  
  294.             // init some stuff
  295.             HWND hProgressTotal = GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL);
  296.             SetWindowLongPtr(hProgressTotal,GWLP_USERDATA,0);
  297.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_STATIC_TOTALTOTAL),GWLP_USERDATA,0);
  298.  
  299.             LockWindowUpdate(hwndDlg);
  300.             InvalidateRect(hProgressTotal,NULL,true);
  301.             UpdateWindow(hProgressTotal);
  302.             LockWindowUpdate(NULL);
  303.             // done initing stuff
  304.  
  305.             HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  306.             // let's set the first column
  307.             LVCOLUMN lvCol;
  308.             lvCol.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
  309.             lvCol.fmt = LVCFMT_LEFT;
  310.             lvCol.cx = 60; // should do the trick
  311.  
  312.             for (int index=0;index < 6;index++)
  313.             {
  314.                 switch(index)
  315.                 {
  316.                     case 0: lvCol.pszText = Translate("Name"); break;
  317.                     case 1: lvCol.pszText = Translate("Author"); break;
  318.                     case 2: lvCol.pszText = Translate("Version"); break;
  319.                     case 3: lvCol.pszText = Translate("Type"); break;
  320.                     case 4: lvCol.pszText = Translate("Packages"); break; // Packages num. format is xx/yy, where xx is main packages, yy is total packages. xx might be bold (?), that'll be nice :)
  321.                     case 5: lvCol.pszText = Translate("Progress"); break;
  322.                 }
  323.                 lvCol.iSubItem = index;
  324.                 ListView_InsertColumn(hListView,index,&lvCol);
  325.             }
  326.  
  327.             // parse the command line here
  328.             // hmm parsing the command line... sounds yummy
  329.             Parse_CommandLine((char*)lParam);
  330.  
  331.             // subclass listview control
  332.             g_lpfListView = (WNDPROC)SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),GWLP_WNDPROC,(LONG_PTR)LVSubClassProc);
  333.  
  334.             // start the DDE server. we use this funky server to receive command lines from multiple instances of mirinst
  335.             // pretty cool if u ask me...
  336.             DdeInitialize(&g_dwDdeInst,(PFNCALLBACK)DdeCallback,APPCLASS_STANDARD | CBF_FAIL_EXECUTES | CBF_FAIL_REQUESTS | CBF_FAIL_SELFCONNECTIONS | CBF_FAIL_ADVISES | CBF_SKIP_ALLNOTIFICATIONS,0);
  337.             DdeNameService(g_dwDdeInst,DdeCreateStringHandle(g_dwDdeInst,DDE_SERVICE,CP_WINANSI),0L,DNS_REGISTER);
  338.  
  339.             LP_TranslateDialog(hwndDlg);
  340.             return(true);
  341.         } break;
  342.         case WM_DRAWITEM:
  343.         {
  344.             // ok we ownerdraw our own little progress bar
  345.             // let's have phun :P
  346.             DRAWITEMSTRUCT *pDrawItem = (DRAWITEMSTRUCT*)lParam;
  347.  
  348.             float fRatio = 0; // percentile
  349.             int iTotal = GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_STATIC_TOTALTOTAL),GWLP_USERDATA);
  350.             if (iTotal == 0) // prevent a division by zero
  351.             {
  352.                 fRatio = 0;
  353.             }
  354.             else
  355.             {
  356.                 fRatio = (float)GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWLP_USERDATA) / (float)iTotal;
  357.             }
  358.  
  359.             // create the new brush... creating on-the-spot sucks, but we don't really have a choice here
  360.             byte bClrProg = (byte)(fRatio * 100);
  361.             HBRUSH hBrush = CreateSolidBrush(RGB(58 + bClrProg,110 + bClrProg,154 + bClrProg));
  362.  
  363.             RECT rcItem;
  364.             CopyMemory(&rcItem,&(pDrawItem->rcItem),sizeof(RECT)); // make a copy
  365.             rcItem.right = (long)((float)(rcItem.right - rcItem.left) * fRatio); // a silly precaution
  366.             FillRect(pDrawItem->hDC,&rcItem,hBrush);
  367.  
  368.             DeleteObject(hBrush);
  369.  
  370.             // add a nice frame. gray is good :)
  371.             FrameRect(pDrawItem->hDC,&(pDrawItem->rcItem),(HBRUSH)GetStockObject(GRAY_BRUSH));
  372.  
  373.             SetWindowLongPtr(hwndDlg,DWLP_MSGRESULT,true);
  374.             return(true);
  375.         } break;
  376.         // msgs from thread
  377.         case MIRINST_INFOMSG_STARTING: // fresh start
  378.         {
  379.             // needed to draw properly later
  380.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_STATIC_TOTALTOTAL),GWLP_USERDATA,ListView_GetItemCount(GetDlgItem(hwndDlg,IDC_INSTALLATIONS)));
  381.             return(true);
  382.         } break;
  383.         case MIRINST_INFOMSG_STARTITEM: // wParam is ID (from ListView) - starting a new installation
  384.         {
  385.             // make sure (argg)
  386.             // tricky, but reawarding - the progress controls userdata portion is the current we have
  387.             // the static text's userdata is the total
  388.             // kinda stupid...
  389.             HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  390.             ListView_SetItemText(hListView,wParam,5,Translate("Starting..."));
  391.             ListView_EnsureVisible(hListView,wParam,false);
  392.             return(true);
  393.         } break;
  394.         case MIRINST_INFOMSG_FINISHEDITEM: // wParam is ID, lParam is 2 if we canceled, lParam is 0 if everything went well, 1 if error
  395.         {
  396.             HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  397.  
  398.             if (lParam == 2)
  399.             {
  400.                 ListView_SetItemText(hListView,wParam,5,Translate("Aborted"));
  401.             }
  402.             else if (lParam == 0) // good!
  403.             {
  404.                 ListView_SetItemText(hListView,wParam,5,Translate("Finished"));
  405.             }
  406.             else // bad!
  407.             {
  408.                 char szPackageName[128];
  409.                 char szMsg[256];
  410.  
  411.                 ListView_GetItemText(hListView,wParam,0,szPackageName,128);
  412.                 sprintf(szMsg,Translate("Error has occured when extracting %s, try redownloading it."),szPackageName);
  413.                 MessageBox(hwndDlg,szMsg,Translate("Miranda Installer Error"),MB_ICONWARNING);
  414.  
  415.                 ListView_SetItemText(hListView,wParam,5,Translate("Failed"));
  416. //                SetWindowLongPtr(GetDlgItem(hwndDlg,IDOK),GWLP_USERDATA,1); // error has occured
  417.             }
  418.  
  419.             // now update total
  420.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWLP_USERDATA,wParam+1); // got package total
  421.             SendMessage(hwndDlg,WM_USER+120,0,0); // update and stuff
  422.             return(true);
  423.         } break;
  424.         case MIRINST_INFOMSG_EXTRACTING: // extracting a file. wParam is package ID. lParam is a pointer to the file name - extraction will begin in a sec
  425.         {
  426.             char szLine[256];
  427.             sprintf(szLine,Translate("Extracting %s..."),(char*)lParam);
  428.             ListView_SetItemText(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),wParam,5,szLine);
  429.             return(true);
  430.         } break;
  431.         case MIRINST_INFOMSG_DONE: // the thread finished work. yippie.
  432.         {
  433.             WaitForSingleObject(g_hWorkThread,INFINITE); // wait for the thread to finish completely
  434.             CloseHandle(g_hWorkThread);
  435.             g_hWorkThread = NULL;
  436.  
  437.             SetDlgItemText(hwndDlg,IDOK,Translate("Start"));
  438.             // LVN_DELETEITEM is working on this
  439. //            EnableWindow(GetDlgItem(hwndDlg,IDOK),false); // nothing more
  440. //            EnableWindow(GetDlgItem(hwndDlg,IDC_STARTMIRANDA),false); // not relevant
  441.  
  442. //            Sleep(1000); // the user should know what happened?... :) ahh
  443.  
  444.             if ((!(g_bInstallFlags & 1)) && (!wParam)) // no error, tell everything is good
  445.             {
  446.                 MessageBox(hwndDlg,Translate("Installation is finished. Click OK to continue."),Translate("Miranda Installer"),MB_ICONINFORMATION);
  447.             }
  448.  
  449.             // only clear after we show dialog (nicer)
  450.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_STATIC_TOTALTOTAL),GWLP_USERDATA,0);
  451.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWLP_USERDATA,0);
  452.             SendMessage(hwndDlg,WM_USER+120,0,0);
  453.  
  454.             // go over list, find any language packs
  455.             WORD wLangPackNum = 0; // count starts here
  456.             HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  457.             LVITEM lvItem;
  458.             lvItem.iSubItem = 0;
  459.             lvItem.mask = LVIF_PARAM;
  460.             for (int index=0,iCount=ListView_GetItemCount(hListView);index < iCount;index++)
  461.             {
  462.                 lvItem.iItem = index;
  463.                 ListView_GetItem(hListView,&lvItem);
  464.  
  465.                 if (((ADDON_LISTINFO*)lvItem.lParam)->bType == INSTALLTYPE_LANGUAGE)
  466.                 {
  467.                     wLangPackNum++;
  468.                 }
  469.             }
  470.  
  471.             ListView_DeleteAllItems(hListView); // I salute you *sniff*
  472.  
  473.             if (wLangPackNum > 0) // we have langpacks from the last install
  474.             {
  475.                 DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_SELECTLANGPACK),hwndDlg,LangPackDlgProc);
  476.             }
  477.  
  478.             if (g_bInstallFlags & 1) // no error, then we shutdown ourselves
  479.             {
  480.                 // okay we're done installing
  481.                 DestroyWindow(hwndDlg);
  482.             }
  483.             return(true);
  484.         } break;
  485.         case WM_USER+112: // change in num of items to install
  486.         {
  487.             EnableWindow(GetDlgItem(hwndDlg,IDOK),wParam); // nothing more
  488.             return(true);
  489.         } break;
  490.         case WM_USER+120: // if wParam is 0, we update the percentile and do other invalidation and update to package progress, if wParam is 1, we touch the total
  491.         {
  492.             // there are a lot of reference to package here
  493.             // package = item
  494.             // it's this way coz this code like came out of the box from wassup updater
  495.             HWND hProgressTotal = GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL);
  496.             LockWindowUpdate(hwndDlg);
  497.             InvalidateRect(hProgressTotal,NULL,true);
  498.             UpdateWindow(hProgressTotal);
  499.             LockWindowUpdate(NULL);
  500.  
  501.             int iTotal = GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_STATIC_TOTALTOTAL),GWLP_USERDATA);
  502.             if (iTotal == 0) // prevent a division by zero
  503.             {
  504.                 SetDlgItemText(hwndDlg,IDC_PROGRESS_TOTALPERCENT,"0%");
  505.             }
  506.             else
  507.             {
  508.                 char szPercentage[6]; // precaution
  509.                 wsprintf(szPercentage,"%d",(long)(((float)GetWindowLongPtr(GetDlgItem(hwndDlg,IDC_PROGRESS_TOTAL),GWLP_USERDATA) / (float)iTotal) * 100.0f));
  510.                 lstrcat(szPercentage,"%"); // not taking any chances
  511.                 SetDlgItemText(hwndDlg,IDC_PROGRESS_TOTALPERCENT,szPercentage);
  512.             }
  513.             return(true);
  514.         } break;
  515.         case WM_COMMAND:
  516.         {
  517.             if (HIWORD(wParam) == BN_CLICKED) // If we clicked
  518.             {
  519.                 switch (LOWORD(wParam)) 
  520.                 {
  521.                     case ID_MAINMENU_ABOUT:
  522.                     {
  523.                         DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_ABOUTDIALOG),hwndDlg,AboutDlgProc);
  524.                         return(true);
  525.                     } break;
  526.                     case ID_MAINMENU_STARTMIRANDA: // start miranda
  527.                     {
  528.                         g_bInstallFlags &= ~2; // if we will have to restart miranda later, then don't
  529.                         Restart_Miranda();
  530.                         return(true);
  531.                     } break;
  532.                     case ID_MAINMENU_INSTALLSINGLE:
  533.                     {
  534.                         // let's first ask where to save
  535.                         OPENFILENAME ofnStruct;
  536.                         char szFilename[MAX_PATH + 1];
  537.                         *szFilename = '\0';
  538.                         ZeroMemory(&ofnStruct,sizeof(OPENFILENAME));
  539.                         ofnStruct.lStructSize = sizeof(OPENFILENAME);
  540.                         ofnStruct.hwndOwner = hwndDlg;
  541.  
  542.                         char szFilter[256];
  543.                         char *lpCurText = szFilter;
  544.                         lstrcpy(lpCurText,Translate("Addons (*.min;*.mir;*.mii;*.mis;*.mil;*.mit;*.mio;*.mic;*.mik)"));
  545.                         lpCurText += lstrlen(lpCurText) + 1;
  546.                         lstrcpy(lpCurText,"*.min;*.mir;*.mii;*.mis;*.mil;*.mit;*.mio;*.mic;*.mik");
  547.                         lpCurText += lstrlen(lpCurText) + 1;
  548.  
  549.                         // zip files
  550.                         lstrcpy(lpCurText,Translate("Zip Files (*.zip)"));
  551.                         lpCurText += lstrlen(lpCurText) + 1;
  552.                         lstrcpy(lpCurText,"*.zip");
  553.                         lpCurText += lstrlen(lpCurText) + 1;
  554.                         
  555.                         // all files
  556.                         lstrcpy(lpCurText,Translate("All Files (*.*)"));
  557.                         lpCurText += lstrlen(lpCurText) + 1;
  558.                         CopyMemory(lpCurText,"*.*\0\0",5);
  559.  
  560.                         ofnStruct.lpstrFilter = szFilter;
  561.                         ofnStruct.lpstrFile = szFilename;
  562.                         ofnStruct.lpstrDefExt = "mir"; // default extension
  563.                         ofnStruct.nMaxFile = MAX_PATH + 1;
  564.                         ofnStruct.lpstrTitle = Translate("Install file...");
  565.                         ofnStruct.Flags = OFN_ENABLESIZING | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  566.                         
  567.                         if (GetOpenFileName(&ofnStruct))
  568.                         {
  569.                             InstallList_AddFile(hwndDlg,ofnStruct.lpstrFile);
  570.                         }
  571.                         return(true);
  572.                     } break;
  573.                     case ID_MAINMENU_INSTALLMULTI:
  574.                     {
  575.                         // more code from wassup
  576.                         // heh :)
  577.                         char szDirectory[MAX_PATH + 1];
  578.  
  579.                         BROWSEINFO browseInfo;
  580.                         ZeroMemory(&browseInfo,sizeof(BROWSEINFO)); // quicker than NULLing everything
  581.                         browseInfo.hwndOwner = hwndDlg;
  582.                         browseInfo.pszDisplayName = szDirectory;
  583.                         browseInfo.lpszTitle = Translate("Select directory:");
  584.                         browseInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_NEWDIALOGSTYLE;
  585.  
  586.                         LPITEMIDLIST idList;
  587.                         if (idList = SHBrowseForFolder(&browseInfo),idList != NULL) // old habit
  588.                         {
  589.                             LPMALLOC psMalloc; // [soon to be] pointer to shell's IMalloc object
  590.                             SHGetMalloc(&psMalloc);
  591.                             SHGetPathFromIDList(idList,szDirectory);
  592.                             psMalloc->Free(idList); // release the damn idList
  593.                             psMalloc->Release();
  594.  
  595.                             // ok now szDirectory is the directory where we're going to put our stuff
  596.                             if (*(szDirectory+lstrlen(szDirectory)-1) != '\\') // no backslash
  597.                             {
  598.                                 lstrcat(szDirectory,"\\"); // must be backslashed already
  599.                             }
  600.                             
  601.                             InstallList_AddDirectory(hwndDlg,szDirectory);
  602.                             return(true);
  603.                         }
  604.                         return(true);
  605.                     } break;
  606.                     case ID_MAINMENU_ASSOCIATE:
  607.                     {
  608.                         // Right now, all file types share the same description
  609.                         // and icon. This can be changed, though.
  610.                         if (!RegisterApplication("MirInst","Miranda Installer"))
  611.                         {
  612.                             MessageBox(hwndDlg,Translate("Could not register application, please try again."),Translate("Miranda Installer Error"),MB_ICONWARNING);
  613.                             return(true); // couldn't register the app, so registering file types makes no sense
  614.                         }
  615.  
  616.                         for (int index=0;INSTALLATION_EXTS_ARR[index] != NULL;index++)
  617.                         {
  618.                             char szExt[5]; // big enough
  619.                             wsprintf(szExt,".%s",INSTALLATION_EXTS_ARR[index]);
  620.                             RegisterExtension(szExt,"MirInst");
  621.                         }
  622.  
  623.                         MessageBox(hwndDlg,Translate("Association of file types was successful!"),Translate("Miranda Installer"),0);
  624.                         return(true);
  625.                     } break;
  626.                     case ID_MAINMENU_EXIT:
  627.                     {
  628.                         DestroyWindow(hwndDlg);
  629.                         return(true);
  630.                     } break;
  631.                     case ID_MAINMENU_OPTIONS:
  632.                     {
  633.                         DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_OPTIONSDIALOG),hwndDlg,OptionsDlgProc);
  634.                         return(true);
  635.                     } break;
  636.                     case ID_MAINMENU_LANGPACKS:
  637.                     {
  638.                         // we pop if we got any kind of langpack present
  639.                         // that's bcoz the option of using no langpacks is also available
  640.                         // then we pop nice dialog
  641.                         char szSearchPath[MAX_PATH + 1];
  642.                         WIN32_FIND_DATA findData;
  643.  
  644.                         wsprintf(szSearchPath,"%s\\langpack_*.txt",g_WorkOptions.szLangDirectory);
  645.  
  646.                         HANDLE hFindFile;
  647.                         bool fLPAvail = false;
  648.                         if (hFindFile = FindFirstFile(szSearchPath,&findData), hFindFile != INVALID_HANDLE_VALUE)
  649.                         {
  650.                             fLPAvail = true;
  651.                             FindClose(hFindFile);
  652.                         }
  653.  
  654.                         if (!fLPAvail) // try miranda's dir
  655.                         {
  656.                             wsprintf(szSearchPath,"%s\\langpack_*.txt",g_WorkOptions.szMirandaDirectory);
  657.                             if (hFindFile = FindFirstFile(szSearchPath,&findData), hFindFile != INVALID_HANDLE_VALUE)
  658.                             {
  659.                                 do
  660.                                 {
  661.                                     fLPAvail = true;
  662.                                     FindClose(hFindFile);
  663.                                     break;
  664.                                 } while (FindNextFile(hFindFile,&findData));
  665.                             }
  666.                         }
  667.  
  668.                         if (fLPAvail) // we got something
  669.                         {
  670.                             DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_SELECTLANGPACK),hwndDlg,LangPackDlgProc);
  671.                         }
  672.                         else
  673.                         {
  674.                             MessageBox(hwndDlg,Translate("There are no installed language packs to manage."),Translate("Miranda Installer"),MB_ICONINFORMATION);
  675.                         }
  676.                         return(true);
  677.                     } break;
  678.                     case IDOK: // install
  679.                     {
  680.                         if (g_hWorkThread == NULL) // not doing anything right now
  681.                         {
  682.                             // is app running
  683.                             HWND hwndMiranda = Is_App_Running(); // figure out if Miranda is running
  684.  
  685.                             if (hwndMiranda != NULL) // Miranda is running
  686.                             {
  687.                                 bool fKillMiranda = false;
  688.                                 if (g_bInstallFlags & 1) // silent installation, just install the damn thing
  689.                                 {
  690.                                     fKillMiranda = true;
  691.                                 }
  692.                                 else if (!(g_WorkOptions.dwFlags & 8)) // if we didn't specify we shouldn't get warnings // ask the user
  693.                                 {
  694.                                     char szTitle[32];
  695.                                     char szMsg[512];
  696.                                     szTitle[GetWindowText(hwndMiranda,szTitle,32)] = '\0'; // make sure it's sealed tight
  697.  
  698.                                     wsprintf(szMsg,Translate("Miranda was found running (The window's title is \"%s\"). This might interfere with the installation. Would you like to close it?"),szTitle);
  699.  
  700.                                     if (MessageBox(hwndDlg,szMsg,Translate("Miranda Installer"),MB_ICONQUESTION | MB_YESNO) == IDYES)
  701.                                     {
  702.                                         fKillMiranda = true;
  703.                                     }
  704.                                     else
  705.                                     {
  706.                                         g_bInstallFlags &= ~2; // don't restart Miranda later, it's already running
  707.                                     }
  708.                                 }
  709.  
  710.                                 if (fKillMiranda) // kill Miranda!
  711.                                 {
  712.                                     // Wassup Updater code
  713.                                     DWORD dwPID;
  714.                                     GetWindowThreadProcessId(hwndMiranda,&dwPID); // get Miranda's PID
  715.  
  716.                                     HANDLE hMirandaProcess = OpenProcess(SYNCHRONIZE,false,dwPID); // we try to get a handle to the process (we use it to wait)
  717.  
  718.                                     // used by Miranda InstallMaker, close Miranda now
  719.                                     // thx kai_b
  720.                                     PostMessage(hwndMiranda,WM_COMMAND,40001,0);
  721.                                     // posting is safer
  722.                                     // no danger there
  723.  
  724.                                     if (hMirandaProcess != NULL)
  725.                                     {
  726.                                         WaitForSingleObject(hMirandaProcess,3000); // 3 secs is enough
  727.                                         CloseHandle(hMirandaProcess);
  728.                                     }
  729.  
  730.                                     // we let Miranda 3 seconds to close itself, that's enough
  731.                                     if (IsWindow(hwndMiranda)) // still there
  732.                                     {
  733.                                         MessageBox(hwndDlg,Translate("Could not close Miranda's window, try again."),Translate("Miranda Installer Error"),MB_ICONWARNING);
  734.                                         return(1);
  735.                                     }
  736.                                     g_bInstallFlags |= 2; // restart miranda later
  737.                                 }
  738.                             }
  739.  
  740.                             DWORD dwTID;
  741.                             SetDlgItemText(hwndDlg,IDOK,Translate("Stop!"));
  742.                             g_hWorkThread = CreateThread(NULL,0,InstallerProc,hwndDlg,0,&dwTID); // create the thread
  743.                         }
  744.                         else // working
  745.                         {
  746.                             g_fStopThread = true;
  747.                         }
  748.                         return(true);
  749.                     } break;
  750.                 }
  751.             }
  752.         } break;
  753.         case WM_INITMENU: // modify menu before it is shown
  754.         {
  755.             bool fGrayed = g_hWorkThread != NULL;
  756.  
  757.             HMENU hMenu = (HMENU)wParam;
  758.             if (hMenu == GetMenu(hwndDlg))
  759.             {
  760.                 EnableMenuItem(hMenu,ID_MAINMENU_LANGPACKS,MF_BYCOMMAND | ((fGrayed) ? (MF_GRAYED) : (MF_ENABLED)));
  761.                 EnableMenuItem(hMenu,ID_MAINMENU_INSTALLSINGLE,MF_BYCOMMAND | ((fGrayed) ? (MF_GRAYED) : (MF_ENABLED)));
  762.                 EnableMenuItem(hMenu,ID_MAINMENU_INSTALLMULTI,MF_BYCOMMAND | ((fGrayed) ? (MF_GRAYED) : (MF_ENABLED)));
  763.                 EnableMenuItem(hMenu,ID_MAINMENU_OPTIONS,MF_BYCOMMAND | ((fGrayed) ? (MF_GRAYED) : (MF_ENABLED)));
  764.                 EnableMenuItem(hMenu,ID_MAINMENU_STARTMIRANDA,MF_BYCOMMAND | ((fGrayed) ? (MF_GRAYED) : (MF_ENABLED)));
  765.  
  766.                 // test for Miranda
  767.                 if (!fGrayed) // not working
  768.                 {
  769.                     MENUITEMINFO menuInfo;
  770.                     menuInfo.cbSize = sizeof(MENUITEMINFO);
  771.                     menuInfo.fMask = MIIM_STRING;
  772.  
  773.                     if (Is_App_Running()) // currently running
  774.                     {
  775.                         menuInfo.dwTypeData = Translate("Start Miranda (Running)");
  776.                     }
  777.                     else
  778.                     {
  779.                         menuInfo.dwTypeData = Translate("Start Miranda");
  780.                     }
  781.  
  782.                     SetMenuItemInfo(hMenu,ID_MAINMENU_STARTMIRANDA,false,&menuInfo);
  783.                 }
  784.             }
  785.             return(0);
  786.         } break;
  787.         case WM_NOTIFY: // Notification messages
  788.         {
  789.             switch(((LPNMHDR)lParam)->code)
  790.             {
  791.                 case LVN_KEYDOWN: // del button
  792.                 {
  793.                     if (g_hWorkThread == NULL)
  794.                     {
  795.                         NMLVKEYDOWN *pKeyDown = (NMLVKEYDOWN*)lParam;
  796.  
  797.                         if (pKeyDown->wVKey == VK_DELETE)
  798.                         {
  799.                             HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  800.                             int iItem = ListView_GetSelectionMark(hListView);
  801.                             if ((iItem != -1) && (ListView_GetSelectedCount(hListView) > 0))
  802.                             {
  803.                                 ListView_DeleteItem(hListView,iItem);
  804.                             }
  805.                         }
  806.                     }
  807.                 } break;
  808.                 case NM_RCLICK:
  809.                 {
  810.                     if ((g_hWorkThread == NULL) && (g_hContextMenu != NULL))
  811.                     {
  812.                         LVHITTESTINFO hitTest;
  813.                         GetCursorPos(&hitTest.pt); // don't use the message info, it could be unavail previous to 4.71
  814.                         ScreenToClient(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),&hitTest.pt);
  815.                         
  816.                         int iItem = ListView_HitTest(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),&hitTest);
  817.  
  818.                         if ((iItem != -1) && (hitTest.flags & LVHT_ONITEM)) // on label+icon
  819.                         {
  820.                             // switch to screen coords again
  821.                             ClientToScreen(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),&hitTest.pt);
  822.                             if (TrackPopupMenu(g_hContextMenu,TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,hitTest.pt.x,hitTest.pt.y,0,hwndDlg,NULL) == 1)
  823.                             {
  824.                                 // delete item
  825.                                 ListView_DeleteItem(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),iItem);
  826.                             }
  827.                         }
  828.                     }
  829.                     return(0);
  830.                 } break;
  831.                 case LVN_DELETEITEM:
  832.                 {
  833.                     // an item is about to be deleted
  834.                     // we must free it!
  835.                     NMLISTVIEW *nmLV = (NMLISTVIEW*)lParam; 
  836.  
  837.                     // we first need to get the lParam
  838.                     LVITEM lvItem;
  839.                     lvItem.mask = LVIF_PARAM;
  840.                     lvItem.iItem = nmLV->iItem;
  841.  
  842.                     ListView_GetItem(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),&lvItem);
  843.  
  844.                     ADDON_LISTINFO *node = (ADDON_LISTINFO*)lvItem.lParam;
  845.                     if (node != NULL)
  846.                     {
  847.                         if (node->lpFilePath != NULL)
  848.                         {
  849.                             delete [] node->lpFilePath;
  850.                         }
  851.                         delete node; // delete the bastard
  852.                     }
  853.  
  854.                     if (ListView_GetItemCount(GetDlgItem(hwndDlg,IDC_INSTALLATIONS)) == 1) // last one
  855.                     {
  856.                         EnableWindow(GetDlgItem(hwndDlg,IDOK),false); // nothing more
  857.                     }
  858.                 } break;
  859.             }
  860.         } break;
  861.         case WM_CLOSE: // block close
  862.         {
  863. /*            if (g_hWorkThread != NULL) // if working, don't leave
  864.             {
  865.                 return(true);
  866.             }*/
  867.             DestroyWindow(hwndDlg);
  868.         } break;
  869.         case WM_DESTROY:
  870.         {
  871.             g_hInstallDlg = NULL;
  872.             if (g_dwDdeInst)
  873.             {
  874.                 DdeNameService(g_dwDdeInst,DdeCreateStringHandle(g_dwDdeInst,DDE_SERVICE,CP_WINANSI),0L,DNS_UNREGISTER);
  875.                 DdeUninitialize(g_dwDdeInst);
  876.             }
  877.  
  878.             SetWindowLongPtr(GetDlgItem(hwndDlg,IDC_INSTALLATIONS),GWLP_WNDPROC,(LONG_PTR)g_lpfListView);
  879.             PostQuitMessage(0);
  880.             return(false);
  881.         } break;
  882.     }
  883.     return(false);
  884. }
  885.  
  886.  
  887.  
  888. // WINMAIN ////////////////////////////////////////////////
  889. int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hprevinstance,LPSTR lpcmdline,int ncmdshow)
  890. {/*
  891.     #ifndef NDEBUG
  892.     int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); // Get current flag
  893.     flag |= _CRTDBG_LEAK_CHECK_DF; // Turn on leak-checking bit
  894.     _CrtSetDbgFlag(flag); // Set flag to the new value
  895.     #endif*/
  896.  
  897.     MSG msg;
  898.  
  899.     // Save the instance
  900.     g_hInstance = hinstance;
  901.  
  902.     HANDLE hMutex = CreateMutex(NULL,true,"MirInst56ERD");
  903.  
  904.     if (GetLastError() == ERROR_ALREADY_EXISTS) // the mutex already exists
  905.     {
  906.         // start session with server, send command line
  907.         DWORD dwInst = 0;
  908.         if (DdeInitialize(&dwInst,(PFNCALLBACK)DdeDummyCallback,APPCMD_CLIENTONLY | APPCLASS_STANDARD | CBF_SKIP_ALLNOTIFICATIONS,0) == DMLERR_NO_ERROR)
  909.         {
  910.             HCONV hConv = DdeConnect(dwInst,DdeCreateStringHandle(dwInst,DDE_SERVICE,CP_WINANSI),DdeCreateStringHandle(dwInst,DDE_TOPIC,CP_WINANSI),NULL);
  911.  
  912.             if (hConv == 0L) // can't connect to DDE server
  913.             {
  914.                 DdeUninitialize(dwInst);
  915.                 return(0);
  916.             }
  917.  
  918.             DWORD dwResult = 0;
  919.             if (!DdeClientTransaction((BYTE*)lpcmdline,lstrlen(lpcmdline)+1,hConv,DdeCreateStringHandle(dwInst,DDE_ITEM,CP_WINANSI),CF_TEXT,XTYP_POKE,5000,&dwResult))
  920.             {
  921.                 DdeUninitialize(dwInst);
  922.                 return(0);
  923.             }
  924.             DdeUninitialize(dwInst);
  925.         }
  926.         CloseHandle(hMutex); // explicitly release the handle
  927.         return(0);
  928.     }
  929.  
  930.     ReleaseMutex(hMutex);
  931.  
  932.     CoInitialize(NULL);
  933.  
  934.     InitCommonControls();
  935.     PackageSelector_Init();
  936.     LP_Init();
  937.  
  938.     // ok other stuff now
  939.     // no point in using a template for the popup menu, maybe if/when it'll get bigger :)
  940.     g_hContextMenu = CreatePopupMenu();
  941.  
  942.     if (g_hContextMenu != NULL)
  943.     {
  944.         // the first item will be added with the [messy] InsertMenuItem, which gives us some more styles we need
  945.         MENUITEMINFO menuItem;
  946.         menuItem.cbSize = sizeof(MENUITEMINFO);
  947.         menuItem.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE;
  948.         menuItem.fType = MFT_STRING;
  949.         menuItem.dwTypeData = Translate("Delete from list");
  950.         menuItem.cch = lstrlen((char*)menuItem.dwTypeData);
  951.         menuItem.wID = 1;
  952.         menuItem.fState = MFS_DEFAULT;
  953.         InsertMenuItem(g_hContextMenu,1,false,&menuItem);
  954.     }
  955.  
  956.     // settings are loaded here
  957.     // build the directory
  958.     char szCDir[MAX_PATH + 1];
  959.     GetModuleFileName(NULL,szCDir,MAX_PATH + 1);
  960.     *(strrchr(szCDir,'\\')+1) = '\0';
  961.     lstrcat(szCDir,"Options.dat");
  962.  
  963.     FILE *hFile = fopen(szCDir,"rb");
  964.  
  965.     ZeroMemory(&g_WorkOptions,sizeof(g_WorkOptions));
  966.     if (hFile != NULL)
  967.     {
  968.         fread((void*)&g_WorkOptions,sizeof(g_WorkOptions),1,hFile);
  969.         fclose(hFile);
  970.     }
  971.  
  972.     if (lstrlen(g_WorkOptions.szMirandaDirectory) == 0) // no good, give notice, then launch options dialog
  973.     {
  974.         MessageBox(NULL,Translate("Before you could use Miranda Installer, you will need to set a few settings"),Translate("Miranda Installer"),MB_ICONINFORMATION);
  975.         DialogBox(g_hInstance,MAKEINTRESOURCE(IDD_OPTIONSDIALOG),NULL,OptionsDlgProc);
  976.     }
  977.  
  978.     // main dialog
  979.     CreateDialogParam(g_hInstance,MAKEINTRESOURCE(IDD_INSTALLDIALOG),NULL,InstallDlgProc,(LPARAM)lpcmdline);
  980.  
  981.     // enter main event loop
  982.     // using better structure
  983.     BOOL bRet;
  984.     while ((bRet = GetMessage(&msg,NULL,0,0)) != 0)
  985.     {
  986.         if (bRet == -1)
  987.         {
  988.             break; // leave
  989.         }
  990.         else
  991.         {
  992.             TranslateMessage(&msg);
  993.             DispatchMessage(&msg);
  994.         }
  995.     }
  996.  
  997.     // save settings
  998.     // using szCDir found before
  999.     hFile = fopen(szCDir,"wb");
  1000.  
  1001.     if (hFile != NULL)
  1002.     {
  1003.         fwrite((void*)&g_WorkOptions,sizeof(g_WorkOptions),1,hFile);
  1004.         fclose(hFile);
  1005.     }
  1006.  
  1007.     if (g_hContextMenu != NULL)
  1008.     {
  1009.         DestroyMenu(g_hContextMenu);
  1010.     }
  1011.  
  1012.     LP_UnInit();
  1013.     CoUninitialize();
  1014.     CloseHandle(hMutex); // good practice
  1015.  
  1016.     if (g_bInstallFlags & 2) // yay, restart
  1017.     {
  1018.         Restart_Miranda();
  1019.     }
  1020.     return(msg.wParam);
  1021. }
  1022.